package org.koon.jadelyre;

import org.jadelyre.core.ende.Ende;
import org.jadelyre.core.ende.Packet;
import org.jadelyre.core.exception.JadelyreException;
import org.jadelyre.utils.ByteBufferUtils;
import org.jadelyre.utils.StrUtils;
import org.koon.*;
import org.koon.exception.KoonException;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public class KoonEnde implements Ende {

    @Override
    public KoonPacket decode(ByteBuffer byteBuffer) throws JadelyreException {
        byteBuffer.order(Packet.BYTE_ORDER);
        //解析请求行
        RequestLine requestLine;
        try {
            requestLine = decodeRequestLine(byteBuffer);
        } catch (KoonException e) {
            throw new JadelyreException(e.getMessage());
        }
        if (requestLine == null){
            return null;
        }
        //解析请求头
        Map<String, String> header = decodeRequestHeader(byteBuffer);
        if (header.isEmpty()){
            return null;
        }
        String contentLengthStr = header.get(Header.CONTENT_LENGTH.getValue().toLowerCase());
        int contentLength = 0;
        if (!StrUtils.isBlank(contentLengthStr)){
            contentLength = Integer.parseInt(contentLengthStr);
        }
        if (byteBuffer.remaining() < contentLength){
            return null;
        }
        KooRequest httpRequest = new KooRequest();
        httpRequest.setRequestLine(requestLine);
        httpRequest.setHeaders(header);
        httpRequest.parseCookie();
        if (!StrUtils.isBlank(requestLine.getQueryString())){
            decodeParam(httpRequest.getParams(),requestLine.getQueryString());
        }
        httpRequest.setContentLength(contentLength);
        if (contentLength>0){
            decodeBodyFormat(httpRequest);
            decodeBody(byteBuffer,httpRequest);
        }
        return httpRequest;
    }

    @Override
    public ByteBuffer encode(Packet packet) {
        MoonResponse httpResponse = (MoonResponse)packet;
        int bodyLength = httpResponse.getBody().length;
        httpResponse.putHeader("server","koon");
        String format = LocalDateTime.now().format(DateTimeFormatter.ofPattern("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US));
        httpResponse.putHeader(Header.DATE.getValue(),format);
        httpResponse.putHeader(Header.CONTENT_TYPE.getValue(), "application/json;charset=UTF-8");
        httpResponse.putHeader(Header.CONTENT_LENGTH.getValue(),Integer.toString(bodyLength));
        HttpStatus httpStatus = httpResponse.getHttpStatus();
        String responseLine = "HTTP/1.1 " + httpStatus.getStatus() + " " + httpStatus.getDescription();
        byte[] responseLineBytes = responseLine.getBytes();
        int responseLineLength = responseLineBytes.length + 2;
        ByteBuffer byteBuffer = ByteBuffer.allocate(responseLineLength +
                httpResponse.getHeaderLength()+ httpResponse.getCookieLength() + bodyLength + 2);
        byteBuffer.order(Packet.BYTE_ORDER);
        byteBuffer.put(responseLineBytes).put(ByteBufferUtils.CR).put(ByteBufferUtils.LF);
        for (Map.Entry<byte[], byte[]> entry : httpResponse.getHeaders().entrySet()) {
            byteBuffer.put(entry.getKey()).put(ByteBufferUtils.COL).put(ByteBufferUtils.SPACE)
                    .put(entry.getValue()).put(ByteBufferUtils.CR).put(ByteBufferUtils.LF);
        }
        for (byte[] bytes : httpResponse.getCookieList()) {
            byteBuffer.put(Header.SET_COOKIE.getValue().getBytes())
                    .put(ByteBufferUtils.COL).put(ByteBufferUtils.SPACE)
            .put(bytes).put(ByteBufferUtils.CR).put(ByteBufferUtils.LF);
        }
        byteBuffer.put(ByteBufferUtils.CR).put(ByteBufferUtils.LF);
        if (bodyLength > 0) {
            byteBuffer.put(httpResponse.getBody());
        }
        byteBuffer.flip();
        return byteBuffer;
    }


    //解析请求行
    private RequestLine decodeRequestLine(ByteBuffer byteBuffer) throws KoonException {
        byte[] bytes = byteBuffer.array();
        int position = byteBuffer.position();
        String methodStr = null;
        String pathStr = null;
        String queryStr = null;
        String protocol = null;
        String version = null;
        int lastPosition = position;
        boolean going = true;
        while (going){
            if (!byteBuffer.hasRemaining()){
                //没有数据或者解析完了却没有解析到http协议，这是一个http请求吗
                throw new KoonException("no request data or It is a http request");
                //return null;
            }
            byte b = byteBuffer.get();
            if (null == methodStr){
                if (ByteBufferUtils.SPACE == b){
                    methodStr = createStringFromBytes(bytes,byteBuffer.position(),lastPosition);
                    lastPosition = byteBuffer.position();
                }
                continue;
            }
            if (null == pathStr){
                if (b == ByteBufferUtils.SPACE || b == ByteBufferUtils.QUESTION_MARK) {
                    pathStr = createStringFromBytes(bytes,byteBuffer.position(),lastPosition);
                    lastPosition = byteBuffer.position();
                    if (b == ByteBufferUtils.SPACE) {
                        queryStr = "";
                    }
                }
                continue;
            }
            if (null == queryStr){
                if (b == ByteBufferUtils.SPACE) {
                    queryStr = createStringFromBytes(bytes,byteBuffer.position(),lastPosition);
                    lastPosition = byteBuffer.position();
                }
                continue;
            }
            if (null == protocol){
                if (b == ByteBufferUtils.FORWARD_SLASH) {
                    protocol = createStringFromBytes(bytes,byteBuffer.position(),lastPosition);
                    lastPosition = byteBuffer.position();
                }
                continue;
            }
            if (null == version){
                if (b == ByteBufferUtils.CR){
                    version = createStringFromBytes(bytes,byteBuffer.position(),lastPosition);
                    lastPosition = byteBuffer.position();
                }
                continue;
            }
            going = false;
        }
        if (methodStr == null || pathStr == null || queryStr == null || protocol == null || version == null){
            return null;
        }
        RequestLine requestLine = new RequestLine();
        RequestMethod method = RequestMethod.from(methodStr);
        requestLine.setMethod(method);
        requestLine.setPath(pathStr);
        requestLine.setQueryString(queryStr);
        requestLine.setProtocol(protocol);
        requestLine.setVersion(version);
        return requestLine;
    }
    //解析请求头
    private Map<String,String> decodeRequestHeader(ByteBuffer byteBuffer){
        byte[] bytes = byteBuffer.array();
        int lastPosition = byteBuffer.position();
        Map<String,String> header = new HashMap<>();
        boolean going = true;
        byte expect = ByteBufferUtils.COL;
        byte endExpect = ByteBufferUtils.CR;
        String key = null;
        String value = null;
        while (going){
            if (!byteBuffer.hasRemaining()){
                break;
            }
            byte b = byteBuffer.get();
            if (expect == ByteBufferUtils.COL && b == endExpect){
                byteBuffer.get();//将最后一个换行符取掉
                return header;
            }
            if (b == expect){
                String s = createStringFromBytes(bytes, byteBuffer.position(), lastPosition);
                lastPosition = byteBuffer.position();
                s:switch (expect){
                    case ByteBufferUtils.COL:
                        key = s;
                        expect = ByteBufferUtils.CR;
                        break s;
                    case ByteBufferUtils.CR:
                        value = s;
                        expect = ByteBufferUtils.COL;
                        lastPosition++;//将/n拿掉
                        break s;
                }
                if (key != null && value != null){
                    header.put(key.toLowerCase(),value.trim());
                }
                continue;
            }
        }
        return header;
    }
    private String createStringFromBytes(byte[] bytes,int position,int lastPosition){
        int length = position - lastPosition - 1;
        return new String(bytes, lastPosition, length);
    }
    private void decodeParam(Map<String, Object[]> params, String queryString){
        String[] keyValues = queryString.split("&");
        for (String keyValue : keyValues) {
            String[] keyValueArr = keyValue.split("=");
            if (keyValueArr.length <2 ){
                continue;
            }
            String key = keyValueArr[0];
            String value = null;
            try {
                value = URLDecoder.decode(keyValueArr[1],KooRequest.DEFAULT_CHARSET);
            } catch (UnsupportedEncodingException e) {
            }
            Object[] existValue = params.get(key);
            if (existValue != null) {
                String[] newExistValue = new String[existValue.length + 1];
                System.arraycopy(existValue, 0, newExistValue, 0, existValue.length);
                newExistValue[newExistValue.length - 1] = value;
                params.put(key, newExistValue);
            } else {
                String[] newExistValue = new String[] { value };
                params.put(key, newExistValue);
            }
        }
    }

    private void decodeBodyFormat(KooRequest kooRequest){
        String contentType = kooRequest.getHeaders().get(Header.CONTENT_TYPE.getValue().toLowerCase());
        if (contentType != null){
            contentType = contentType.toLowerCase();
        }
        if (contentType.startsWith(Header.ContentType.JSON.getValue())){
            kooRequest.setBodyFormat(BodyFormat.TEXT);
        }else if (contentType.startsWith(Header.ContentType.MULTIPART.getValue())){
            kooRequest.setBodyFormat(BodyFormat.MULTIPART);
        }else {
            kooRequest.setBodyFormat(BodyFormat.URLENCODED);
        }
        String charset = KooRequest.DEFAULT_CHARSET;
        String charset_ = extractValueFromEqPair(contentType, "charset");
        if (charset_ != null){
            charset = charset_;
        }
        kooRequest.setCharset(charset);
    }
    public static String extractValueFromEqPair(String eqPair, String name){
        String[] kv = eqPair.split(";");
        for (String s : kv) {
            if (s.trim().startsWith(name)){
                String[] split = s.split("=");
                String value = split[1];
                if (value.charAt(0) == ('"')){
                    char[] chars = value.toCharArray();
                    return String.copyValueOf(chars, 1, chars.length - 2);
                }
                return split[1];
            }
        }
        return null;
    }
    private void decodeBody(ByteBuffer byteBuffer, KooRequest httpRequest){
        if (httpRequest.getBodyFormat() == BodyFormat.MULTIPART){
            String contentType = httpRequest.getHeaders().get(Header.CONTENT_TYPE.getValue().toLowerCase());
            String boundary = extractValueFromEqPair(contentType, "boundary");
            MultiBodyDecoder.decode(byteBuffer,boundary,httpRequest);
        }else {
            byte[] bytes = new byte[httpRequest.getContentLength()];
            byteBuffer.get(bytes);
            String bodyString = null;
            if (bytes != null && bytes.length > 0) {
                try {
                    bodyString = new String(bytes, httpRequest.getCharset());
                    httpRequest.setBodyString(bodyString);
                    httpRequest.setBody(bytes);
                } catch (UnsupportedEncodingException e) {
                }
            }

            if (httpRequest.getBodyFormat() == BodyFormat.URLENCODED) {
                decodeParam(httpRequest.getParams(), bodyString);
            }
        }
    }
}
